/*
 * detectface.cpp
 *
 *  Created on: 2016年3月17日
 *      Author: guyadong
 */
#include <utility>
#include <mutex>
#include <cmath>
#include <memory>
#include "detectface.h"
#include "intrinsic_wrapper.h"
#include "file_utilits.h"
#include "detect_param.h"
#include "display_tool.h"
#include "jpeg_mem.h"
namespace gdface {
/*
 * 计算指定检测窗口的目标分布范围(矩形)尺寸(对于同一个目标实际检测到的目标位置的分布范围)
 */
template<typename R=size_t>
static inline R get_dist_size(size_t win_size,size_t win_size0=DETECT_WIN_SIZE,size_t dist_size0=MIN_DIST_SIZE){
	assert(win_size>=win_size0);
	return R(std::round(float(dist_size0)*float(win_size)/win_size0));
}
/*
 * 计算指定检测窗口的采样步长
 */
template<typename R=size_t>
static inline R get_sample_step(size_t win_size,size_t win_size0=DETECT_WIN_SIZE){
	assert(win_size>=win_size0);
	//return std::min<R>(R(1U)<<_bsr_int32_(uint32_t(win_size/win_size0+1)),R(16));
	return std::max<R>(R((win_size/win_size0)&~(2ULL-1)),R(2));
}
/*
 * 计算指定目标分布范围(矩形窗口)的目标总数判定阀值(>=该阀值即判定为目标)
 */
template<typename R=size_t>
inline R get_sum_threshold(size_t sample_step,size_t dist_size,size_t dist_size0=MIN_DIST_SIZE,size_t sum_threshold0=MIN_SUM_THRESHOLD){
	assert(size_t(dist_size)>=dist_size0);
	return R(std::round(float(sum_threshold0)*float(dist_size)/dist_size0/sample_step/sample_step));
}

/*
 * 从detected_objects_buffer中读取检测到的对象数据，根据归一化因子normalization_factor转换为归一化的矩形数据
 */
object_info_vector create_return_vector_norm(const detected_objects_buffer_norm &object_buffer ,const cl_float2& normalization_factor){
	throw_if(MAX_DETECTED_OBJECT_NUM<=object_buffer.detected_num||object_buffer.status!=KERNEL_OK)
	object_info_vector v(object_buffer.detected_num);
	const auto max_f=std::max<cl_float>(normalization_factor.x,normalization_factor.y);
	const auto factor=cl_float2{normalization_factor.x/max_f,normalization_factor.y/max_f};
	for(size_t i=0;i<v.size();++i){
		auto obj=object_buffer.storage[i];
		v[i].norm={obj.x,obj.y,obj.w*factor.x,obj.w*factor.y};
	}
	return v;
}

/*
 * 从detected_objects_buffer中读取检测到的对象数据，根据图像尺寸转换为默认的的矩形数据
 */
object_info_vector create_return_vector(const detected_objects_buffer_norm &object_buffer, const gf_int2 &img_size){
	const cl_float2 normalization_factor={1.0f/img_size.x,1.0f/img_size.y};
	auto objects=create_return_vector_norm(object_buffer,normalization_factor);
	return convert_def(objects,img_size);
}
std::vector<feather_block> detect_face::make_feathers_block(size_t max_block_size) {
	// 每一层的特征数必须小于max_block_size，否则抛出异常
	for (decltype(cas_base.CasNum) layer = 0; layer < cas_base.CasNum; ++layer) {
		throw_if_msg(size_t(cas_base.FeaNumPerLayer[layer]) > max_block_size, "to small max_block_size")
	}
	std::vector<feather_block> const_feather;
	std::vector<size_t> prefix_sum_layer(cas_base.CasNum);
	// 计算前缀和
	for (decltype(cas_base.CasNum) i = 0, sum = 0; i < cas_base.CasNum; ++i)
		sum += cas_base.FeaNumPerLayer[i], prefix_sum_layer[i] = sum;
	for (size_t start_layer = 0, end_layer = 1, last_sum = 0; end_layer <= prefix_sum_layer.size(); ++end_layer) {
		if (end_layer == prefix_sum_layer.size()) {
			const_feather.emplace_back(
					feather_block { start_layer, end_layer, start_layer > 0 ? prefix_sum_layer[start_layer - 1] : 0,
							struct_vector_cl<feather_short_cl> { } });
		} else if (prefix_sum_layer[end_layer] - last_sum > max_block_size) {
			const_feather.emplace_back(
					feather_block { start_layer, end_layer, start_layer > 0 ? prefix_sum_layer[start_layer - 1] : 0,
							struct_vector_cl<feather_short_cl> { } });
			start_layer = end_layer;
			last_sum = prefix_sum_layer[end_layer - 1];
		}
	}
	return const_feather;
}
detect_face::detect_face(const casinfo_cl& cas, facecl_context& context) :
		context(context), cas_base(to_casinfo_base_float(dynamic_cast<const casinfo_base&>(cas)),
				CL_MEM_READ_ONLY | CL_MEM_COPY_HOST_PTR, context.getContext()), feathers(cas.Feather,
				CL_MEM_READ_ONLY | CL_MEM_COPY_HOST_PTR, context.getContext()), feathers_short(
				convert_short(cas.Feather), CL_MEM_READ_ONLY | CL_MEM_COPY_HOST_PTR, context.getContext()) {
	if (!context.device_type_is(CL_DEVICE_TYPE_CPU)) {
		auto const_size = std::min<size_t>(size_t(tls::constant_buffer_size() / sizeof(decltype(feathers_short)::value_type)),
				size_t(cas_base.FeatherNumPerCas));
		auto block_template = make_feathers_block(const_size);
		v_feathers_constant = std::vector<std::vector<feather_block>>(
				feathers_short.size() / cas_base.FeatherNumPerCas);
		auto ptr = feathers_short.data();
		std::vector<size_t> prefix_sum_layer(cas_base.CasNum);
		// 计算前缀和
		for (decltype(cas_base.CasNum) i = 0, sum = 0; i < cas_base.CasNum; ++i)
			sum += cas_base.FeaNumPerLayer[i], prefix_sum_layer[i] = sum;
		for (decltype(v_feathers_constant.size()) level = 0; level < v_feathers_constant.size(); ++level, ptr += cas_base.FeatherNumPerCas) {
			// 根据全局常量缓冲区的大小(constant buffer size)决定 constant feather sub buffer的大小
			v_feathers_constant[level] = block_template;
			for (size_t i = 0; i < block_template.size(); ++i) {
				auto &block = v_feathers_constant[level][i];
				v_feathers_constant[level][i].feathers =
						struct_vector_cl<decltype(feathers_short)::value_type>(ptr + block.offset,
								ptr + (block.end_layer > 0 ? prefix_sum_layer[block.end_layer - 1] : 0)).createBuffer(
								CL_MEM_READ_ONLY | CL_MEM_COPY_HOST_PTR, context.getContext());
			}
		}
	}
}
detect_face::detect_face(facecl_context& context):context(context){}
detect_face& detect_face::operator =(const detect_face& lv) {
	throw_if(std::addressof(lv.context)!=std::addressof(this->context))
	this->cas_base = lv.cas_base;
	this->feathers = lv.feathers;
	this->feathers_short = lv.feathers_short;
	this->v_feathers_constant = lv.v_feathers_constant;
	return *this;
}
detect_face& detect_face::operator =(detect_face&& rv) {
	throw_if(std::addressof(rv.context)!=std::addressof(this->context))
	this->cas_base = std::move(rv.cas_base);
	this->feathers = std::move(rv.feathers);
	this->feathers_short = std::move(rv.feathers_short);
	this->v_feathers_constant = std::move(rv.v_feathers_constant);
	return *this;
}
/* 根据积分图尺寸创建输出矩阵,
 * tuple中第一个matrix_cl矩阵描述对应的位置是否检测到人脸，0为无，非0为有
 * 第二个matrix_cl矩阵描述对应的位置的积分图平均值
 * 第三个matrix_cl矩阵描述对应的位置的积分图方差
 * */
detect_face::out_mat_type detect_face::create_out_matrixs(size_t integral_width, size_t integral_height,
		size_t detect_win_size) const {
	throw_if(integral_width <= detect_win_size || integral_height <= detect_win_size)			// 输出矩阵宽度和高度都要减去检测窗口尺寸
	// |          out matrix width |detect win size|
	// +---------------------------+---------------+
	// |                           |               |
	// |                           |               |
	// |                           |               |
	// |out matrix height          |               |
	// |                           |               |
	// |                           |               |
	// |                           |               |
	// |                           |               | src img matrix height
	// |                           |               |
	// |                           |               |
	// |                           |               |
	// +---------------------------+            ---+---
	// |                                           |
	// |                                           |
	// |                          detect win size  |
	// |                                           |
	// |                                           |
	// |                                           |
	// +-------------------------------------------+---
	//            src img matrix width
	// 为避免平均值、均方差、分类器读取数据越界，检测范围长宽各减1
	matrix_info_cl minfo = { cl_uint(integral_width - detect_win_size - 1), cl_uint(
			integral_height - detect_win_size - 1) };
	minfo.row_stride = align_up_plus(minfo.width, detect_win_size, INTEGRAL_ALIGN_2POWER);
	hit_mat_type hit_mat(minfo, context.getContext());
	mean_mat_type mean_mat(minfo, context.getContext());
	variance_mat_type variance_mat(minfo, context.getContext());
	return out_mat_type(std::move(hit_mat), std::move(mean_mat), std::move(variance_mat));
}
/*
 * 根据采样步长(sample_step)创建缩小的输出矩阵
 */
detect_face::out_mat_type detect_face::create_out_matrixs(size_t integral_width, size_t integral_height,
		size_t detect_win_size, size_t sample_step) const {
	throw_if(integral_width <= detect_win_size || integral_height <= detect_win_size)			// 输出矩阵宽度和高度都要减去检测窗口尺寸
	// |          out matrix width |detect win size|
	// +---------------------------+---------------+
	// |                           |               |
	// |                           |               |
	// |                           |               |
	// |out matrix height          |               |
	// |                           |               |
	// |                           |               |
	// |                           |               |
	// |                           |               | src img matrix height
	// |                           |               |
	// |                           |               |
	// |                           |               |
	// +---------------------------+            ---+---
	// |                                           |
	// |                                           |
	// |                          detect win size  |
	// |                                           |
	// |                                           |
	// |                                           |
	// +-------------------------------------------+---
	//            src img matrix width
	// 为避免平均值、均方差、分类器读取数据越界，检测范围长宽各减1
	matrix_info_cl minfo = { cl_uint(ceil_div(integral_width - detect_win_size - 1, sample_step)), cl_uint(
			ceil_div(integral_height - detect_win_size - 1, sample_step)) };
	minfo.row_stride = align_up_plus(minfo.width, detect_win_size, INTEGRAL_ALIGN_2POWER);
	hit_mat_type hit_mat(minfo, context.getContext());
	mean_mat_type mean_mat(minfo, context.getContext());
	variance_mat_type variance_mat(minfo, context.getContext());
	return out_mat_type(std::move(hit_mat), std::move(mean_mat), std::move(variance_mat));
}
/*
 * 根据积分图尺寸创建全尺寸hit_mat_type矩阵
 */
detect_face::hit_mat_type detect_face::create_hit_full_matrix(size_t integral_width, size_t integral_height,
		size_t detect_win_size) const {
	throw_if(integral_width <= detect_win_size || integral_height <= detect_win_size)			// 输出矩阵宽度和高度都要减去检测窗口尺寸
	// |          out matrix width |detect win size|
	// +---------------------------+---------------+
	// |                           |               |
	// |                           |               |
	// |                           |               |
	// |out matrix height          |               |
	// |                           |               |
	// |                           |               |
	// |                           |               |
	// |                           |               | src img matrix height
	// |                           |               |
	// |                           |               |
	// |                           |               |
	// +---------------------------+            ---+---
	// |                                           |
	// |                                           |
	// |                          detect win size  |
	// |                                           |
	// |                                           |
	// |                                           |
	// +-------------------------------------------+---
	//            src img matrix width
	matrix_info_cl minfo = { cl_uint(integral_width - detect_win_size), cl_uint(integral_height - detect_win_size) };
	minfo.row_stride = align_up_plus(minfo.width, detect_win_size, INTEGRAL_ALIGN_2POWER);
	return hit_mat_type(minfo, context.getContext());
}

detect_face::integral_matrixs_type detect_face::create_integral(const gray_matrix_cl& img) const {
	return integral_matrixs_type(img.integral<float, INTEGRAL_ALIGN_2POWER, integ_default>(),
			img.integral<float, INTEGRAL_ALIGN_2POWER, integ_square>());
}
/*
 * 创建目标密度矩阵
 * */
detect_face::density_mat_type detect_face::create_density_matrix(const hit_mat_type& hit_mat, size_t detect_win_size) const {
	// 目标密度矩阵宽度和高度都要减去目标分布窗口尺寸
	// |      density matrix width |  dist win size|
	// +---------------------------+---------------+
	// |                           |               |
	// |                           |               |
	// |                           |               |
	// |density matrix height      |               |
	// |                           |               |
	// |                           |               |
	// |                           |               |
	// |                           |               | integral count matrix height
	// |                           |               |
	// |                           |               |
	// |                           |               |
	// +---------------------------+            ---+---
	// |                                           |
	// |                                           |
	// |                          dist win size    |
	// |                                           |
	// |                                           |
	// |                                           |
	// +-------------------------------------------+---
	//            integral count matrix width
	auto dist_size = get_dist_size<cl_int>(detect_win_size, global_measure.detect_win_size[0]);
	auto sample_step = get_sample_step(detect_win_size, global_measure.detect_win_size[0]);
	auto ic_row_stride = align_up_plus(hit_mat.width, dist_size, INTEGRAL_ALIGN_2POWER);
	auto integ_count_mat = hit_mat.integral_count(-(int) (ic_row_stride));
	// 为避免读取数据越界，检测范围长宽各减1
	density_mat_type density_mat(integ_count_mat.width - dist_size - 1, integ_count_mat.height - dist_size - 1,
			-align_up_plus((int) ((integ_count_mat.width - dist_size - 1)), dist_size, INTEGRAL_ALIGN_2POWER),
			context.getContext());
	if (tls::device_type_is(CL_DEVICE_TYPE_CPU)) {
		run_kernel(context.getKernel(KERNEL_NAME_VAR(object_density_filter,)),
				cl::EnqueueArgs(tls::command_queue(),
						cl::NDRange { compute_num_groups(density_mat.height, DENSITY_LOCAL_SIZE) * DENSITY_LOCAL_SIZE },
						cl::NDRange { DENSITY_LOCAL_SIZE }), false, dynamic_cast<matrix_info_cl&>(integ_count_mat),
				integ_count_mat, dynamic_cast<matrix_info_cl&>(density_mat), density_mat, dist_size,
				get_sum_threshold<cl_ushort>(sample_step, dist_size));
	} else {
		run_kernel(context.getKernel(KERNEL_NAME_VAR(object_density_filter,)),
				cl::EnqueueArgs(tls::command_queue(),
						cl::NDRange { align_up(ceil_div(density_mat.width, 8), _bsr_int32_(uint32_t(DENSITY_FILTER_LOCAL_SIZE))),
								density_mat.height }, cl::NDRange { DENSITY_FILTER_LOCAL_SIZE, 1 }), false,
				dynamic_cast<matrix_info_cl&>(integ_count_mat), integ_count_mat,
				dynamic_cast<matrix_info_cl&>(density_mat), density_mat, dist_size,
				get_sum_threshold<cl_ushort>(sample_step, dist_size));
	}
	return density_mat;
}
/*
 * 检测目标聚类
	 */
void detect_face::cobject_cluster(
		density_mat_type& dest_mat
		,struct_cl<detected_objects_buffer_norm>& detected_objects
		,const cl_int2 &img_size
		,const size_t detect_win_size)const{

	if(tls::device_type_is(CL_DEVICE_TYPE_CPU)){
		cluster_filter_const_param const_param{img_size,cl_int(detect_win_size), get_dist_size<cl_int>(detect_win_size,global_measure.detect_win_size[0])};
		run_kernel(
				context.getKernel(KERNEL_NAME_VAR(object_cluster,))
				,cl::EnqueueArgs(tls::command_queue(),{ 1 })//只有一个work_iteam
				,false
				,dynamic_cast<matrix_info_cl&>(dest_mat)
				,dest_mat
				,detected_objects
				,const_param
			);
	}else{
		auto sample_step = cl_int(cl_int(get_sample_step(detect_win_size, global_measure.detect_win_size[0])));
		struct_cl<cluster_filter_const_param1> const_param(
				  {{cl_float(1)/img_size.x,cl_float(1)/img_size.y},cl_int(detect_win_size), get_dist_size<cl_int>(detect_win_size,global_measure.detect_win_size[0]),sample_step}
				, CL_MEM_COPY_HOST_PTR| CL_MEM_READ_ONLY
				, tls::context());
		auto detected_objects_level=make_detected_objects_buffer_level();
		run_kernel(
				context.getKernel(KERNEL_NAME_VAR(object_cluster_group,))
				, cl::EnqueueArgs(tls::command_queue()
					, cl::NDRange{ align_up(ceil_div(dest_mat.width,sample_step),3),align_up(ceil_div(dest_mat.height,sample_step),3) }
					, cl::NDRange{8,8})
				,false
				,dynamic_cast<matrix_info_cl&>(dest_mat)
				,dest_mat
				,*detected_objects_level
				,const_param
			);
		run_kernel(
				context.getKernel(KERNEL_NAME_VAR(object_cluster,))
				,cl::EnqueueArgs(tls::command_queue(), cl::NDRange{ MAX_DETECTED_OBJECT_NUM_LEVEL })
				,false
				,*detected_objects_level
				,detected_objects
				,const_param
			);
	}
}

/*
 * 执行haar特征过滤器
 * */
detect_face::hit_mat_type detect_face::haar_feather_filter(const detect_param& param,
		const integral_matrixs_type& integ_matrixs, struct_cl<detected_objects_buffer_norm>& detected_objects) const {
	auto& command_queue = tls::command_queue();
	auto sample_step = cl_int(cl_int(get_sample_step(param.detect_win_size, global_measure.detect_win_size[0])));
	if (tls::device_type_is(CL_DEVICE_TYPE_CPU)) {
		// 根据检测窗口放大级数detect_scale_level计算对应的feather的起始地址
		auto &feather = feathers[param.detect_scale_level * this->cas_base.FeatherNumPerCas];
		auto out_matrixs = create_out_matrixs(std::get<0>(integ_matrixs).width, std::get<0>(integ_matrixs).height,
				param.detect_win_size);
		haar_filter_const_param haar_param = {
				{cl_float(1) / std::get<0>(integ_matrixs).width, cl_float(1) / std::get<0>(integ_matrixs).height }
				,cl_int(param.detect_win_size)
				, sample_step
				, cl_float(feather.DValve * feather.DValve)
				, cl_float(1) / (param.detect_win_size * param.detect_win_size)
				,cl_uint(param.detect_scale_level) };
		run_kernel(context.getKernel(KERNEL_NAME_VAR(haar_filter,)),
				cl::EnqueueArgs(
						command_queue
						//每个work-group处理多行,每个work-group 的work_item数目由 HAAR_LOCAL_SIZE 控制
						,cl::NDRange { compute_num_groups(std::get<0>(out_matrixs).height, HAAR_LOCAL_SIZE)* HAAR_LOCAL_SIZE }
						,cl::NDRange { HAAR_LOCAL_SIZE })
				,false
				,detected_objects
				,this->cas_base
				,this->feathers
				,dynamic_cast<const matrix_info_cl&>(std::get<0>(integ_matrixs))
				,std::get<0>(integ_matrixs)
				,std::get<1>(integ_matrixs)
				,dynamic_cast<const matrix_info_cl&>(std::get<0>(out_matrixs))
				,std::get<0>(out_matrixs)
				,haar_param);
		return std::move(std::get<0>(out_matrixs));
	} else {
		auto out_matrixs = create_out_matrixs(std::get<0>(integ_matrixs).width, std::get<0>(integ_matrixs).height,
				param.detect_win_size, sample_step);
		run_kernel(context.getKernel(KERNEL_NAME_VAR(create_mean,)),
				cl::EnqueueArgs(command_queue,
						cl::NDRange { align_up(ceil_div(std::get<0>(out_matrixs).width, 4),
								_bsr_int32_(uint32_t(MEAN_LOCAL_SIZE))), std::get<0>(out_matrixs).height }, cl::NDRange {
								MEAN_LOCAL_SIZE, 1 }), false,
				dynamic_cast<const matrix_info_cl&>(std::get<0>(integ_matrixs)), std::get<0>(integ_matrixs),
				dynamic_cast<const matrix_info_cl&>(std::get<1>(out_matrixs)), std::get<1>(out_matrixs),
				cl_int(param.detect_win_size), cl_float(1) / (param.detect_win_size * param.detect_win_size),
				sample_step);
		run_kernel(context.getKernel(KERNEL_NAME_VAR(create_variance,)),
				cl::EnqueueArgs(command_queue,
						cl::NDRange { align_up(ceil_div(std::get<0>(out_matrixs).width, 4),
								_bsr_int32_(uint32_t(VARIANCE_LOCAL_SIZE))), std::get<0>(out_matrixs).height }, cl::NDRange {
								VARIANCE_LOCAL_SIZE, 1 }), false,
				dynamic_cast<const matrix_info_cl&>(std::get<1>(integ_matrixs)), std::get<1>(integ_matrixs),
				dynamic_cast<const matrix_info_cl&>(std::get<1>(out_matrixs)), std::get<1>(out_matrixs),
				std::get<2>(out_matrixs), cl_int(param.detect_win_size),
				cl_float(1) / (param.detect_win_size * param.detect_win_size), sample_step);
		run_kernel(context.getKernel(KERNEL_NAME_VAR(create_hit16,)), cl::EnqueueArgs(command_queue, cl::NDRange {
				ceil_div(std::get<0>(out_matrixs).width, 16), std::get<0>(out_matrixs).height }), false,
				dynamic_cast<const matrix_info_cl&>(std::get<0>(out_matrixs)), std::get<0>(out_matrixs),
				std::get<2>(out_matrixs));
		//show_hit_mat(std::get<0>(out_matrixs));
		run_kernel(context.getKernel(KERNEL_NAME_VAR(intersect_filter,)), cl::EnqueueArgs(command_queue, cl::NDRange {
				std::get<0>(out_matrixs).width, std::get<0>(out_matrixs).height }), false, detected_objects,
				dynamic_cast<const matrix_info_cl&>(std::get<0>(integ_matrixs)),
				dynamic_cast<const matrix_info_cl&>(std::get<0>(out_matrixs)), std::get<0>(out_matrixs),
				cl_int(param.detect_win_size),
				cl_float2 { cl_float(1) / std::get<0>(integ_matrixs).width, cl_float(1)
						/ std::get<0>(integ_matrixs).height }, sample_step);
		//show_hit_mat(std::get<0>(out_matrixs));

		hit_mat_type hit_mat_out(std::get<0>(out_matrixs), context.getContext());
		hit_mat_out.fillBuffer(0);
		auto &blocks = this->v_feathers_constant[param.detect_scale_level];
		for (size_t i = 0; i < blocks.size(); ++i) {
			run_kernel(context.getKernel(KERNEL_NAME_VAR(haar_filter,)),
				cl::EnqueueArgs(command_queue,
					cl::NDRange { align_up(std::get<0>(out_matrixs).width,
									_bsr_int32_(uint32_t(HAAR_FILTER_LOCAL_WIDTH))), align_up(std::get<0>(out_matrixs).height,
									_bsr_int32_(uint32_t(HAAR_FILTER_LOCAL_HEIGHT))) }
					, cl::NDRange { HAAR_FILTER_LOCAL_WIDTH,HAAR_FILTER_LOCAL_HEIGHT })
				, false
				, this->cas_base
				, blocks[i].feathers
				, dynamic_cast<const feather_block_base&>(blocks[i])
				, dynamic_cast<const matrix_info_cl&>(std::get<0>(integ_matrixs))
				, std::get<0>(integ_matrixs)
				, dynamic_cast<const matrix_info_cl&>(std::get<0>(out_matrixs))
				, std::get<0>(out_matrixs)
				, std::get<2>(out_matrixs)
				, hit_mat_out
				, sample_step);
			//show_hit_mat(hit_mat_out);
		}

		auto hit_mat_full = create_hit_full_matrix(std::get<0>(integ_matrixs).width, std::get<0>(integ_matrixs).height,
				param.detect_win_size);
		hit_mat_full.fillBuffer(cl_uchar(0));
		run_kernel(context.getKernel(KERNEL_NAME_VAR(create_hit_mat_full,)),
				cl::EnqueueArgs(command_queue,
						cl::NDRange { std::get<0>(out_matrixs).width, std::get<0>(out_matrixs).height }), false,
				dynamic_cast<const matrix_info_cl&>(std::get<0>(out_matrixs)), hit_mat_out,
				dynamic_cast<const matrix_info_cl&>(hit_mat_full), hit_mat_full, sample_step);
		return hit_mat_full;
	}
}
void detect_face::detect_on_integral(const detect_param& param, const integral_matrixs_type& integ_matrixs,
		struct_cl<detected_objects_buffer_norm>& object_buffer, gray_matrix_cl& zoom) const {
	hit_mat_type hit_mat = haar_feather_filter(param, integ_matrixs, object_buffer);
	show_hit_mat(hit_mat);
	auto density_mat = create_density_matrix(hit_mat, param.detect_win_size);
	if (global_run_config.debug_show) {
		tls::command_queue().finish();
		object_buffer.download_if_need();
	}
	auto last_obj_num = object_buffer.detected_num;
	cobject_cluster(density_mat, object_buffer, { cl_int(zoom.width), cl_int(zoom.height) }, param.detect_win_size);
	show_detect_objects_on_img(zoom, object_buffer, last_obj_num, 0);
}
std::shared_ptr<struct_cl<detected_objects_buffer_norm> > detect_face::make_detected_objects_buffer_norm() const {
	// 为目标检测结果输出缓冲区分配内存
	auto object_buffer_ptr = std::make_shared<struct_cl<detected_objects_buffer_norm> >();
	object_buffer_ptr->detected_num = 0;
	object_buffer_ptr->status = KERNEL_OK;
	object_buffer_ptr->createBuffer(CL_MEM_COPY_HOST_PTR | CL_MEM_READ_WRITE, context.getContext());
	return object_buffer_ptr;
}

std::shared_ptr<struct_cl<detected_objects_buffer_level> > detect_face::make_detected_objects_buffer_level() const {
	// 为目标检测结果输出缓冲区分配内存
	auto object_buffer_ptr = std::make_shared<struct_cl<detected_objects_buffer_level> >();
	object_buffer_ptr->detected_num = 0;
	object_buffer_ptr->status = KERNEL_OK;
	object_buffer_ptr->createBuffer(CL_MEM_COPY_HOST_PTR | CL_MEM_READ_WRITE, context.getContext());
	return object_buffer_ptr;
}

object_info_vector detect_face::detect(const gray_matrix_cl& img, const detect_param& param) const {
	throw_if(std::min<cl_uint>(img.width, img.height) <= param.detect_win_size)
	auto plans = make_detect_plan(img, param);
	// 为目标检测结果输出缓冲区分配内存
	auto object_buffer_ptr = make_detected_objects_buffer_norm();
	detect_param work_param = param;
	if (param.min_first) {
		// min_first为true时将数组反序
		std::reverse(plans.begin(), plans.end());
	}
	gray_matrix_cl zoom;
	for (auto task : plans) {
		// 缩放比例为1时，直接将img引用赋值给img_ref;
		auto& img_ref = 1.0f == task.first ? const_cast<gray_matrix_cl&>(img) : zoom = img.zoom(task.first);
		auto integ_matrixs = create_integral(img_ref);
		auto range = param.min_first ? scale_index_type { task.second.second, task.second.first } : task.second;
		for (int level = range.first; param.min_first ? level < range.second : level > range.second;
				param.min_first ? ++level : --level) {
			work_param.detect_scale_level = level;
			work_param.detect_win_size = global_measure.detect_win_size[level];
			detect_on_integral(work_param, integ_matrixs, *object_buffer_ptr, img_ref);
		}
	}
	tls::command_queue().finish();
	object_buffer_ptr->download_if_need();
	throw_except_if_msg(face_cl_exception, object_buffer_ptr->status != KERNEL_OK,
			kernel_error_msg(object_buffer_ptr->status))
	return create_return_vector_norm(*object_buffer_ptr, { 1.0f / img.width, 1.0f / img.height });
}
#if 0
/*
 * 制定检测计划
 */
detect_face::detect_plan detect_face::make_detect_plan(const size_t img_size,const detect_param &param){
	throw_if(param.detect_win_shrink_floor<DETECT_WIN_SIZE||param.detect_win_shrink_floor>=img_size)
	detect_plan plan;
	const auto real_shrink_floor_index = global_measure.index_of_measure_ceil(param.detect_win_shrink_floor);
	throw_except_if_msg(face_exception, real_shrink_floor_index>=global_measure.detect_win_size.size()-1,"too large detect_win_shrink_floor")
	const auto detect_range_index = param.build_detect_index(img_size);
	const auto floor_win_index=detect_range_index.second;
	auto upper_win_index=detect_range_index.first;
	throw_except_if_msg(face_exception,real_shrink_floor_index>=detect_range_index.first,"too large detect_win_shrink_floor.");
	// 缩小倍数从16倍开始向下(16,8,4,2)
	for(auto shrink=4;shrink>0;--shrink){
		auto upper_size = global_measure.get_detect_win_size_by_index(upper_win_index);
		if((global_measure.detect_win_size[real_shrink_floor_index]<<shrink)>=upper_size)
			continue;
		auto floor_index=global_measure.index_of_measure_floor(global_measure.detect_win_size[real_shrink_floor_index]<<shrink);
		assert(floor_index>=0);
		auto shrinked_upper_index=global_measure.index_of_measure_floor(upper_size>>shrink);
		auto shrinked_floor_index=floor_index-(upper_win_index-shrinked_upper_index);// 与upper_win_index同步减小
		// 缩小之后的检测窗口还是还是大于global_measure.detect_win_size的容量，则图像太大，抛出异常
		throw_except_if_msg(face_exception,shrinked_upper_index>=global_measure.detect_win_size.size(),"too large detect window,please increase magnify level of feather.")
		auto shrinked_ratio=float(global_measure.detect_win_size[shrinked_upper_index])/ upper_size;
		plan.emplace_back(shrinked_ratio,std::make_pair(shrinked_upper_index,shrinked_floor_index));
		upper_win_index = floor_index;
	}
	if (floor_win_index < upper_win_index) {
		const auto upbound = global_measure.detect_win_size.size()-1;
		while (upper_win_index > upbound) {
			// 处理超过最大检测级数的部分,以real_shrink_floor_index为检测下限索引
			auto c = int(std::min(upbound- real_shrink_floor_index, upper_win_index- upbound));// 计算任务级数
			auto u = real_shrink_floor_index +c;
			plan.emplace_back(float(global_measure.detect_win_size[u]) / global_measure.get_detect_win_size_by_index(upper_win_index)
					, std::make_pair(u, real_shrink_floor_index));
			upper_win_index -= c;
		}
		// 剩余级数不需要放大
		plan.emplace_back(1.0f, std::make_pair(upper_win_index, floor_win_index));
	}
	return plan;
}
#else
/*
 * 制定检测计划
 */
detect_face::detect_plan detect_face::make_detect_plan(const size_t img_size, const detect_param& param) {
	return detect_plan { { 1.0f, build_detect_index(param,img_size) } };
}
#endif
detect_face::detect_plan detect_face::make_detect_plan(const gray_matrix_cl& img, const detect_param& param) {
	return make_detect_plan(std::min<cl_uint>(img.width, img.height), param);
}

detect_face::detect_plan detect_face::make_detect_plan(const gf_int2& img_size, const detect_param& param) {
	return make_detect_plan(std::min<int32_t>(img_size.w, img_size.h), param);
}
static detect_face _detect;
void detect_cl::init(float step,size_t scale_size){
	try{
		// 用std::once_flag信号量控制初始化只能被执行一次
		static std::once_flag oc;
		std::call_once(oc, [&] {  
			if (scale_size != global_measure.detect_win_size.size() || step != global_measure.step) {
				init_global_measure(scale_size, step, DETECT_WIN_SIZE);
			}
			global_facecl_context = facecl_context(DEFAULT_CL_ROOT);
		});
		
#ifdef CASINFO_EMBEDDED
		auto cas=load_casinfo_from_code().build_feathers(global_measure);
#else
		auto cas=load_casinfo_file("CascadeInfo").build_feathers(global_measure);
#endif

		_detect=detect_face(cas);
		return;
	}catch(cl::Error &e){
		PRINT_CL_ERROR_COUT(e);
	}catch(face_cl_exception &e){
		cout<<e.what()<<endl;
	}catch(std::exception &e){
		cout<<"exception:"<<e.what()<<endl;
	}catch(...){
		cout<<"exception:unknow"<<endl;
	}
	exit(1);
}
/*
 * image_matrix转换为image_matrix_param
 * */
inline image_matrix_param to_image_matrix_param(const image_matrix& src){
	image_matrix_param dst;
	*(image_matrix_base *)(&dst)=*(const image_matrix_base *)(&src);
	dst.pixels=std::vector<uint8_t>(src.pixels,src.pixels+get_image_matrix_size(src));
	return dst;
}

object_info_vector detect_cl::detect(const image_matrix& img, const detect_param& param) {
	// 如果img不是灰度图像，则先转灰
	if(img.color_space!=GFCS_GRAYSCALE){
		auto gray_img=to_gray_image_matrix(to_image_matrix_param(img));
		return _detect.detect(gray_matrix_cl (reinterpret_cast<image_matrix_v&&>(gray_img)),param);
	}else
		return _detect.detect(gray_matrix_cl(img),param);
}
#if defined(GF_DLL_BUILD)||_MSC_VER>=1900 || __cplusplus>=201103L
object_info_vector detect_cl::detect(image_matrix_v&& img, const detect_param& param){
	if(img.color_space!=GFCS_GRAYSCALE){
		return detect(img,param);
	}else
		return _detect.detect(gray_matrix_cl(std::move(img)),param);
}
#endif
void detect_cl::set_global_run_config(bool debug_show, bool kerne_message) {
	global_run_config.debug_show = debug_show;
	global_run_config.kerne_message = kerne_message;
}
} /* namespace gdface */
